using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DailyPoetryX.Models;
using Newtonsoft.Json;
using SQLite;
using Xamarin.Forms.Internals;

namespace DailyPoetryX.Services.Implementations {
    /// <summary>
    /// 同步服务。
    /// </summary>
    public class SyncService : ISyncService {
        /******** 公开变量 ********/

        /******** 私有变量 ********/

        /// <summary>
        /// 收藏存储。
        /// </summary>
        private IFavoriteStorage _localFavoriteStorage;

        /// <summary>
        /// 远程收藏存储。
        /// </summary>
        private IRemoteFavoriteStorage _remoteFavoriteStorage;

        /******** 继承方法 ********/

        /// <summary>
        /// 状态。
        /// </summary>
        public string Status {
            get => _status;
            set {
                _status = value;
                StatusChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private string _status;

        /// <summary>
        /// 状态改变事件。
        /// </summary>
        public event EventHandler StatusChanged;

        /// <summary>
        /// 远程收藏存储。
        /// </summary>
        public IRemoteFavoriteStorage RemoteFavoriteStorage =>
            _remoteFavoriteStorage;

        /// <summary>
        /// 同步。
        /// </summary>
        public async Task<ServiceResult> SyncAsync() {
            Status = "正在读取本地收藏项";
            var localList =
                (await _localFavoriteStorage.GetFavoriteItemsAsync())
                .Select(p => new TrackableFavorite(p)).ToList();

            Status = "正在读取远程收藏项";
            var remoteList =
                (await _remoteFavoriteStorage.GetFavoriteItemsAsync())
                .Select(p => new TrackableFavorite(p)).ToList();

            Status = "正在合并远程收藏项到本地";
            Dictionary<int, TrackableFavorite> localDictionary =
                new Dictionary<int, TrackableFavorite>();
            await Task.Run(() => {
                localDictionary =
                    localList.ToDictionary(p => p.PoetryId, p => p);

                foreach (var remoteFavorite in remoteList) {
                    if (localDictionary.TryGetValue(remoteFavorite.PoetryId,
                        out var localFavorite)) {
                        if (remoteFavorite.Timestamp >
                            localFavorite.Timestamp) {
                            localFavorite.IsFavorite =
                                remoteFavorite.IsFavorite;
                            localFavorite.Timestamp = remoteFavorite.Timestamp;
                        }
                    } else {
                        localDictionary[remoteFavorite.PoetryId] =
                            remoteFavorite.CloneAsUpdated();
                    }
                }
            });

            Status = "正在保存本地收藏项";
            foreach (var updatedLocalFavorite in localDictionary.Values.Where(
                p => p.Updated)) {
                await _localFavoriteStorage.SaveFavoriteAsync(
                    updatedLocalFavorite, false);
            }

            Status = "正在合并本地收藏项到远程";
            Dictionary<int, TrackableFavorite> remoteDictionary =
                new Dictionary<int, TrackableFavorite>();
            await Task.Run(() => {
                remoteDictionary =
                    remoteList.ToDictionary(p => p.PoetryId, p => p);

                foreach (var localFavorite in localList) {
                    if (remoteDictionary.TryGetValue(localFavorite.PoetryId,
                        out var remoteFavorite)) {
                        if (localFavorite.Timestamp >
                            remoteFavorite.Timestamp) {
                            remoteFavorite.IsFavorite =
                                localFavorite.IsFavorite;
                            remoteFavorite.Timestamp = localFavorite.Timestamp;
                        }
                    } else {
                        remoteDictionary[localFavorite.PoetryId] =
                            localFavorite.CloneAsUpdated();
                    }
                }
            });

            var saveResult =
                await _remoteFavoriteStorage.SaveFavoriteItemsAsync(
                    remoteDictionary.Values.Select(p => p as Favorite)
                        .ToList());
            if (saveResult.Status != ServiceResultStatus.Ok) {
                return saveResult;
            }

            return new ServiceResult {Status = ServiceResultStatus.Ok};
        }

        /******** 公开方法 ********/

        /// <summary>
        /// 同步服务。
        /// </summary>
        /// <param name="localFavoriteStorage">本地收藏存储。</param>
        /// <param name="remoteFavoriteStorage">远程收藏存储。</param>
        public SyncService(IFavoriteStorage localFavoriteStorage,
            IRemoteFavoriteStorage remoteFavoriteStorage) {
            _localFavoriteStorage = localFavoriteStorage;
            _remoteFavoriteStorage = remoteFavoriteStorage;

            remoteFavoriteStorage.StatusChanged += (sender, args) =>
                Status = remoteFavoriteStorage.Status;
        }

        /******** 私有方法 ********/
    }

    /// <summary>
    /// 可追踪收藏类。
    /// </summary>
    [Table("Favorite")]
    class TrackableFavorite : Favorite {
        /******** 公开变量 ********/

        /// <summary>
        /// 是否收藏。
        /// </summary>
        public override bool IsFavorite {
            get => base.IsFavorite;
            set {
                base.IsFavorite = value;
                Updated = true;
            }
        }

        /// <summary>
        /// 时间戳。
        /// </summary>
        public override long Timestamp {
            get => base.Timestamp;
            set {
                base.Timestamp = value;
                Updated = true;
            }
        }

        /// <summary>
        /// 是否更新。
        /// </summary>
        [Ignore]
        [JsonIgnore]
        public bool Updated { get; private set; }

        /******** 私有变量 ********/

        /******** 继承方法 ********/

        /******** 公开方法 ********/

        /// <summary>
        /// 可追踪收藏类。
        /// </summary>
        /// <param name="favorite">收藏。</param>
        public TrackableFavorite(Favorite favorite) {
            PoetryId = favorite.PoetryId;
            base.IsFavorite = favorite.IsFavorite;
            base.Timestamp = favorite.Timestamp;
        }

        /// <summary>
        /// 克隆为已更新的可追踪收藏。
        /// </summary>
        public TrackableFavorite CloneAsUpdated() {
            return new TrackableFavorite(this) {Updated = true};
        }

        /******** 私有方法 ********/
    }
}